package com.course.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.course.component.enums.DateFormats;
import com.course.component.exceptions.FileException;
import com.course.component.http.requ.RequestFile;
import com.course.component.util.AliOSSUtil;
import com.course.component.util.Base64ToMultipartFile;
import com.course.component.util.SnowFlake;
import com.course.entity.File;
import com.course.service.FileService;
import com.course.service.FilesUploadService;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Copyright: Copyright (c)  林中小鸟  All rights reserved.
 * @author: moon
 * @date: 2022/9/23 16:12
 * @Description: **********************
 */
@Service
public class FilesUploadServiceImpl implements FilesUploadService {
    @Value("${file.file-storage}")
    private Path fileStorageLocation; // 文件在本地存储的地址
    @Value("${file.file-download}")
    private Path fileDownloadLocation; // 文件在本地存储的地址
    @Value("${file.file-oss}")
    private Boolean fileOss; // 文件在本地存储的地址
    @Autowired
    private FileService fileModelService;

    @Autowired
    private SnowFlake snowFlake;

    public HashMap uploadFiles(MultipartFile file){
        Map fileRes = storeFile(file);
        String fileName = (String) fileRes.get("fileName");
        String fileDownloadUri = (String) fileRes.get("file_download_uri");
        HashMap res = new HashMap();
        res.put("file_name",fileName);
        res.put("file_download_uri",fileDownloadUri);
        res.put("type",file.getContentType());
        res.put("size",file.getSize());
        return  res;
    }

    public Map<String,String> storeFile(MultipartFile file,Boolean oss){
        if(oss){
            Map<String,String> res = new HashMap<>();
            String originalFilename = file.getOriginalFilename();
            //返回图片的url
            String fileName = AliOSSUtil.uploadFileInputSteam(originalFilename, (java.io.File) file);
            res.put("fileName",fileName);
            res.put("file_download_uri",fileName);
            return res;
        }else{
            return storeFile(file);
        }
    }

    /**
     * 存储文件到系统
     *
     * @param shard 文件
     * @return map
     */
    public Map<String,String> storeFileShard(RequestFile shard) {
        Map<String,String> res = new HashMap<>();
        String md5 = shard.getMd5();
        //如果含有就直接返回
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("file_md5",md5);
        File hasFile = fileModelService.getOne(queryWrapper,false);
        if(hasFile != null && hasFile.getShardIndex()>=shard.getShardIndex()){
            res.put("fileName",hasFile.getFileName());
            res.put("file_download_uri",hasFile.getFilePath());
            return res;
        }
        String shardBase64 = shard.getShard();
        MultipartFile file = Base64ToMultipartFile.base64ToMultipart(shardBase64);
        //上传文件名
        String upName = StringUtils.cleanPath(file.getOriginalFilename());
        Date time = new Date();
        SimpleDateFormat format = new SimpleDateFormat(DateFormats.yyyyMMdd.getFormat());
        String sourcePath = format.format(time) + "-" +String.valueOf(snowFlake.nextId());
        //存储文件名
        String fileName =  sourcePath + "."+ upName.substring(upName.lastIndexOf(".") + 1);
        //本地存储切片名称
        String localFileName = fileName + "." + shard.getShardIndex();
        try {
            // Check if the file's name contains invalid characters
            if(localFileName.contains("..")) {
                throw new FileException(500,"Sorry! Filename contains invalid path sequence " + fileName,false);
            }
            // Copy file to the target location (Replacing existing file with the same name)
            Path targetLocation = this.fileStorageLocation.resolve(localFileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException ex) {
            throw new FileException(500,"Could not store file " + fileName + ". Please try again!", ex);
        }
        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/"+ this.fileDownloadLocation +"/")
                .path(fileName)
                .toUriString();
        if(hasFile == null){
            //存储到文件数据库
            File saveFile = new File();
            saveFile.setUid("1");
            saveFile.setFileName(fileName);
            saveFile.setFilePath(fileDownloadUri);
            saveFile.setFileMd5(md5);
            saveFile.setShardIndex(shard.getShardIndex());
            saveFile.setShardTotal(shard.getShardTotal());
            saveFile.setShardSize(shard.getShardSize());
            saveFile.setFileSource(sourcePath);
            saveFile.setCreateTime(new Date());
            fileModelService.save(saveFile);
            res.put("fileName",fileName);
            //return fileName;
        }else{
            hasFile.setShardIndex(shard.getShardIndex());
            fileModelService.updateById(hasFile);
            res.put("fileName",hasFile.getFileName());
            //return hasFile.getName();
        }
        res.put("file_download_uri",fileDownloadUri);
        return res;
    }

    /**
     * 存储文件到系统
     *
     * @param file 文件
     * @return map 切面 oss 开关 文件名
     */
    public Map<String,String> storeFile(MultipartFile file) {
        Map<String,String> res = new HashMap<>();
        if(this.fileOss.equals(true)){
            String originalFilename = file.getOriginalFilename();
            //返回图片的url
            String fileName = AliOSSUtil.uploadFileInputSteam(originalFilename, file);
            res.put("fileName",fileName);
            res.put("file_download_uri",fileName);
            return res;
        }
        // Normalize file name
        //原始文件名称直接存储
        String upName = StringUtils.cleanPath(file.getOriginalFilename());
        //自定义文件名称
        Date time = new Date();
        SimpleDateFormat format = new SimpleDateFormat(DateFormats.yyyyMMdd.getFormat());
        String sourcePath = format.format(time) + "-" +String.valueOf(snowFlake.nextId());
        String fileName =  sourcePath + "."+ upName.substring(upName.lastIndexOf(".") + 1);
        try {
            String md5 = DigestUtils.md5Hex(file.getInputStream());
            //如果含有就直接返回
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.like("file_md5",md5);
            File hasFile = fileModelService.getOne(queryWrapper,false);
            if(!ObjectUtils.isEmpty(hasFile)){
                res.put("fileName",hasFile.getFileName());
                res.put("file_download_uri",hasFile.getFilePath());
                return res;
            }
            // Check if the file's name contains invalid characters
            if(fileName.contains("..")) {
                throw new FileException(500,"Sorry! Filename contains invalid path sequence " + fileName,false);
            }

            // Copy file to the target location (Replacing existing file with the same name)
            Path targetLocation = this.fileStorageLocation.resolve(fileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
            String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                    .path("/"+ this.fileDownloadLocation +"/")
                    .path(fileName)
                    .toUriString();
            if(ObjectUtils.isEmpty(hasFile)){
                //存储到文件数据库
                File saveFile = new File();
                saveFile.setUid("1");
                saveFile.setFileName(fileName);
                saveFile.setFilePath(fileDownloadUri);
                saveFile.setFileMd5(md5);
                saveFile.setShardIndex(0);
                saveFile.setShardTotal(0);
                saveFile.setShardSize(0);
                saveFile.setFileSource(sourcePath);
                saveFile.setCreateTime(new Date());
                fileModelService.save(saveFile);
            }
            res.put("fileName",fileName);
            res.put("file_download_uri",fileDownloadUri);
            return res;
        } catch (IOException ex) {
            System.out.println("123231");
            throw new FileException(500,"Could not store file " + fileName + ". Please try again!", ex);
        }
    }

    /**
     * 加载文件
     * @param fileName 文件名
     * @return 文件
     */
    public Resource loadFileAsResource(String fileName) {
        try {
            Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if(resource.exists()) {
                return resource;
            } else {
                throw new FileException(500,"File not found " + fileName,false);
            }
        } catch (MalformedURLException ex) {
            throw new FileException(500,"File not found " + fileName, ex);
        }
    }
}

